package com.chenglong.supermarket.product.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.chenglong.supermarket.product.vo.Catelog2Vo;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.chenglong.supermarket.common.utils.PageUtils;
import com.chenglong.supermarket.common.utils.Query;

import com.chenglong.supermarket.product.dao.CategoryDao;
import com.chenglong.supermarket.product.entity.CategoryEntity;
import com.chenglong.supermarket.product.service.CategoryService;
import org.springframework.util.StringUtils;


@Service("categoryService")
public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService {
    @Autowired
    StringRedisTemplate redisTemplate;

   /* todo  @Autowired
    RedissonClient redissonClient;
*/
    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<CategoryEntity> page = this.page(
                new Query<CategoryEntity>().getPage(params),
                new QueryWrapper<CategoryEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public List<CategoryEntity> listWithTree() {
        //1.查出所有分类
        List<CategoryEntity> entities = baseMapper.selectList(null);
        //2.组装成父子的树形结构
        List<CategoryEntity> level1Menus = entities.stream().filter((categoryEntity) -> {
            return categoryEntity.getParentCid() == 0;
        }).map((menue) -> {
            menue.setChildern(getChildren(menue, entities));
            return menue;
        }).sorted((menue1, menue2) -> {
            return (menue1.getSort() == null ? 0 : menue1.getSort()) - (menue2.getSort() == null ? 0 : menue2.getSort());
        }).collect(Collectors.toList());
        return level1Menus;
    }

    @Override
    public void removueMenuByIds(List<Long> asList) {
        //todo 检查当前删除的菜单是否被别的地方引用
        //逻辑删除
        //1.p配置文件中引入全局逻辑删除规则
        //2.配置逻辑删除的bean（省略）
        //3.

        baseMapper.deleteBatchIds(asList);
    }

    @Override
    public List<CategoryEntity> getLevel1Catagorys() {
        List<CategoryEntity> entities = baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));
        return entities;
    }
    @Override
    public Map<String, List<Catelog2Vo>> getCatalogJson(){
        //todo RLock lock = redissonClient.getLock("catalogJSON");
        Map<String, List<Catelog2Vo>> stringListMap = null;
        String catalogJSON = redisTemplate.opsForValue().get("catalogJSON");
        if (StringUtils.isEmpty(catalogJSON)) {
            log.trace("缓存没命中，准备查询数据库");
            String uuid = UUID.randomUUID().toString();
            try {
               //todo lock.lock();
                    log.trace("获取到锁，查库");
                    Map<String, List<Catelog2Vo>> catalogJsonFromDb = getCatalogJsonFromDb();
                    catalogJSON = JSON.toJSONString(catalogJsonFromDb);
                    //查到的数据放到缓存
                    redisTemplate.opsForValue().set("catalogJSON", catalogJSON);
                    stringListMap = JSON.parseObject(catalogJSON, new TypeReference<Map<String, List<Catelog2Vo>>>() {
                    });

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
               //todo  lock.unlock();
            }
        } else {
            stringListMap = JSON.parseObject(catalogJSON, new TypeReference<Map<String, List<Catelog2Vo>>>() {
            });
        }

        return stringListMap;
    }
    /**
     * 自己使用redis设计的分布式锁
     * @return
     */

    public Map<String, List<Catelog2Vo>> getCatalogJsonUseRedisSelfDesign() {
        Map<String, List<Catelog2Vo>> stringListMap = null;
        String catalogJSON = redisTemplate.opsForValue().get("catalogJSON");
        if (StringUtils.isEmpty(catalogJSON)) {
            log.trace("缓存没命中，准备查询数据库");
            String uuid = UUID.randomUUID().toString();
            Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 30, TimeUnit.SECONDS);
            //在删除锁的时候，需要确定删除的时自己之前设置的锁 所以要用uuid标识
            //如果业务时间过长可能要考虑锁续期
            try {
                if (lock) {
                    log.trace("获取到锁，查库");
                    Map<String, List<Catelog2Vo>> catalogJsonFromDb = getCatalogJsonFromDb();
                    catalogJSON = JSON.toJSONString(catalogJsonFromDb);
                    //查到的数据放到缓存
                    redisTemplate.opsForValue().set("catalogJSON", catalogJSON);
                    stringListMap = JSON.parseObject(catalogJSON, new TypeReference<Map<String, List<Catelog2Vo>>>() {
                    });
                } else {
                    System.out.println("锁获取失败，重试");
                    stringListMap = getCatalogJson();
                }

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                //这时还是有删错别人锁的风险
                /*String lockValue = redisTemplate.opsForValue().get("lock");
                if(uuid.equals(lockValue)){
                    redisTemplate.delete("lock");
                }*/
                String script = "if redis.call('get',KEYS[1]) == ARG[1] then return redis.call('del',KEYS[1]) else retrun 0 end";
                //原子删锁
                redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList("lock"), uuid);
            }
        } else {
            stringListMap = JSON.parseObject(catalogJSON, new TypeReference<Map<String, List<Catelog2Vo>>>() {
            });
        }

        return stringListMap;
    }

    /**
     * 数据库查询三级分类
     *
     * @return
     */

    public Map<String, List<Catelog2Vo>> getCatalogJsonFromDb() {
        //查出所有分类
        List<CategoryEntity> categoryEntities = baseMapper.selectList(null);
        //封装数据
        Map<String, List<Catelog2Vo>> map = categoryEntities.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
            List<CategoryEntity> categoryEntities1 = baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", v.getCatId()));
            List<Catelog2Vo> catelog2Vos = null;
            if (!StringUtils.isEmpty(categoryEntities)) {
                catelog2Vos = categoryEntities1.stream().map(item -> {
                    Catelog2Vo catelog2Vo = new Catelog2Vo(v.getCatId().toString(), null, item.getCatId().toString(), item.getName());
                    List<CategoryEntity> categoryEntities2 = baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", item.getCatId()));
                    List<Catelog2Vo.catalog3Vo> catalog3Vos = null;
                    if (!StringUtils.isEmpty(categoryEntities2)) {
                        catalog3Vos = categoryEntities2.stream().map(tiem2 -> {
                            Catelog2Vo.catalog3Vo catalog3Vo = new Catelog2Vo.catalog3Vo();
                            catalog3Vo.setCatalog2Id(item.getCatId().toString());
                            catalog3Vo.setId(tiem2.getCatId().toString());
                            catalog3Vo.setName(tiem2.getName());
                            return catalog3Vo;
                        }).collect(Collectors.toList());
                    }
                    catelog2Vo.setCatalog3List(catalog3Vos);
                    return catelog2Vo;
                }).collect(Collectors.toList());
            }
            return catelog2Vos;
        }));
        return map;
    }

    /**
     * 递归查找子菜单
     *
     * @param entity
     * @param list
     * @return
     */
    private List<CategoryEntity> getChildren(CategoryEntity entity, List<CategoryEntity> list) {
        List<CategoryEntity> children = list.stream().filter((categoryEntity) -> {
            return (long) categoryEntity.getParentCid() == (long) entity.getCatId();
        }).map((categoryEntity) -> {
            //1.找到子菜单
            categoryEntity.setChildern(getChildren(categoryEntity, list));
            return categoryEntity;
        }).sorted((menu1, menu2) -> {
            return (menu1.getSort() == null ? 0 : menu1.getSort()) - (menu2.getSort() == null ? 0 : menu2.getSort());
        }).collect(Collectors.toList());
        return children;
    }
}